package net.cyndeline.GUI.graphics

import scala.collection.mutable.ArrayBuffer
import javafx.scene.image.{Image, ImageView}

/**
 * An object containing multiple images, stepping through them on at the time then loops.
 * @param frameDelay The number of frames the animation should pause after a new frame has been displayed, before
 *                   the next is selected.
 * @param firstFrame The first frame in the animation, input separately so empty animations cannot be created.
 * @param frames The remaining frames in the animation, in the order they should be displayed.
 */
class Animation(frameDelay: Int, firstFrame: Image, frames: Image*) {
  private var frameCount = 0   // Counts how many frames the current frame has been displayed
  private var stopped = true   // Animation must be started manually before the frames start to cycle
  private var currentFrame = 0 // Which frame in the frame list that is currently displayed

  // All frames must adhere to the same size. Cast from double to int to ease comparisons
  //TODO test
  val frameWidth = firstFrame.getWidth.toInt
  val frameHeight = firstFrame.getHeight.toInt

  if (frameWidth % 2 != 0 || frameHeight % 2 != 0)
    throw new IllegalArgumentException(
      "Animation frames must have a width and height that is evenly divisible by 2, currently " + frameWidth + " / " + frameHeight + ")"
    )

  private val frameBuffer = new ArrayBuffer[Frame]()
  for (frame <- List(firstFrame) ++ frames) {
    checkSize(frame)
    frameBuffer += makeFrame(frame)
  }

  private val totalFrames = frameBuffer.size

  def start() {
    if (!stopped) return
    else if (totalFrames < 1) return

    stopped = false
  }

  def stop() {
    if (totalFrames < 1) return

    stopped = true
  }

  def restart() {
    if (totalFrames < 1) return

    stopped = false
    currentFrame = 0
  }

  def reset() {
    stopped = true
    currentFrame = 0
    frameCount = 0
  }

  /**
   * @return The image that is currently displayed by the animation.
   */
  def currentSprite: Image = frameBuffer(currentFrame).image

  /**
   * Move the animation ahead by one tick, should be called every frame.
   * @return True if the animation is running and just looped back to the beginning, otherwise false.
   *         Use if there's a need to count the number of times the animation completes.
   */
  def update(): Boolean = {
    if (!stopped) {
      frameCount += 1

      if (frameCount >= frameDelay) {
        frameCount = 0
        currentFrame += 1
      }

      if (currentFrame >= totalFrames) {
        currentFrame = 0
        true
      }
    }

    false
  }

  private def makeFrame(img: Image): Frame = {
    currentFrame = 0
    new Frame(img)
  }

  private def checkSize(frame: Image): Boolean = {
    if (frame.getWidth.toInt != frameWidth || frame.getHeight.toInt != frameHeight) {
      throw new IllegalArgumentException("Every frame in the animation must have the same size in pixels as the first frame (" +
        frameWidth + " / " + frameHeight + "), frame found that has size (" +
        frame.getWidth.toInt + " / " + frame.getHeight.toInt + ")")
    }

    true
  }
}
